package sys.knot.util;

import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.controller.client.OperationBuilder;
import org.jboss.as.controller.client.helpers.ClientConstants;
import org.jboss.dmr.ModelNode;
import org.jboss.logging.Logger;
import sys.knot.configuration.ConfigurationException;
import sys.knot.data.access.NamedDataSource;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class DataSourceHelper {

    private static int managementPort = -1;
    private static Logger logger = Logger.getLogger(DataSourceHelper.class);

    public List<NamedDataSource> getMatchingDataSourcees(List<String> regexMatchers) throws ConfigurationException {
        List<NamedDataSource> result = new ArrayList<>();
        try {
            Set<String[]> availableDataSources = getAllDatasources();

            for (String[] available : availableDataSources) {
                for (String regex : regexMatchers) {
                    if(available[0].matches(regex)) {
                        result.add(getNamedDataSource(available[0], available[1]));
                        break;
                    }
                }
            }
        } catch (IOException e) {
            throw new ConfigurationException("There was as error while trying to list the data sources.", e);
        }

        if (result.isEmpty()) {
            throw new ConfigurationException("Could not find any datasources matching: " + regexMatchers);
        }

        return result;
    }

    private NamedDataSource getNamedDataSource(String dataSourceName, String dataSourceJndi) throws ConfigurationException {
        try {
            InitialContext initialContext = new InitialContext();
            return new NamedDataSource(dataSourceName, (DataSource) initialContext.lookup(dataSourceJndi));
        } catch (NamingException e) {
            throw new ConfigurationException("Could not load datasources.", e);
        }
    }

    // Not pretty, but it works...
    private Set<String[]> getAllDatasources() throws IOException, ConfigurationException {
        Set<String[]> result = new HashSet<>();

        final ModelNode request = new ModelNode();
        request.get(ClientConstants.OP).set("read-resource");
        request.get("recursive").set(true);
        request.get(ClientConstants.OP_ADDR).add("subsystem", "datasources");
        ModelControllerClient client = null;
        try {
            client = ModelControllerClient.Factory.create(InetAddress.getByName("127.0.0.1"), getManagementPort());
            final ModelNode response = client.execute(new OperationBuilder(request).build());
            reportFailure(response);

            for (String key : response.get(ClientConstants.RESULT).get("data-source").keys()) {
                String[] val = {key,
                response.get(ClientConstants.RESULT).get("data-source").get(key).get("jndi-name").asString()};
                result.add(val);
            }

        } finally {
            client.close();
        }

        return result;
    }
    private void reportFailure(final ModelNode node) throws ConfigurationException {
        if (!node.get(ClientConstants.OUTCOME).asString().equals(ClientConstants.SUCCESS)) {
            final String msg;
            if (node.hasDefined(ClientConstants.FAILURE_DESCRIPTION)) {
                if (node.hasDefined(ClientConstants.OP)) {
                    msg = String.format("Operation '%s' at address '%s' failed: %s", node.get(ClientConstants.OP), node.get(ClientConstants.OP_ADDR), node.get(ClientConstants.FAILURE_DESCRIPTION));
                } else {
                    msg = String.format("Operation failed: %s", node.get(ClientConstants.FAILURE_DESCRIPTION));
                }
            } else {
                msg = String.format("Operation failed: %s", node);
            }
            logger.error("While reading data sources, Wildfly says: " + msg);
            throw new ConfigurationException("There was as error while trying to list the data sources.");
        }
    }

    public int getManagementPort() throws ConfigurationException {
        if (managementPort < 0) {
//            try {
//
//                MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
//                ObjectName http = new ObjectName("jboss.as:socket-binding-group=standard-sockets,socket-binding=management-native");
//                logger.info("Management port: " + managementPort + http + mBeanServer);
//                managementPort = (int) mBeanServer.getAttribute(http, "boundPort");
//            } catch (Exception e) {
//                throw new ConfigurationException("Could not find management port: " + e.getMessage(), e);
//            }

            managementPort = Integer.parseInt(System.getProperty("management.port", "9990"));

            logger.debug("Using management port: " + managementPort);
        }

        return managementPort;

    }
}
